package com.github.cyf.excel.util.export;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author chenyifan
 * @create 2025-05-27 17:44
 */
@Component
@Slf4j
public class ExcelExportUtil {

    // 缓存 Class 对应的字段信息（避免重复反射）
    private static final Map<Class<?>, List<FieldInfo>> FIELD_CACHE = new ConcurrentHashMap<>();

    private static final DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");

    // -------------------------------------- 基于注解导出 ---------------------------------------------
    /**
     * 导出 Excel 文件
     * @param dataList  数据列表
     * @param <T>       泛型类型
     */
    public static <T> void export(List<T> dataList, Class<T> clazz, String filePath) {
        File expportFile = new File(filePath);
        if (!expportFile.getParentFile().exists()) {
            expportFile.getParentFile().mkdirs();
        }
        try(FileOutputStream fos = new FileOutputStream(expportFile);
            Workbook workbook = new SXSSFWorkbook()) {
            setData(workbook, "Sheet1", dataList, clazz, fos);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 导出 Excel 文件, 返回文件流
     *
     * @param dataList  数据列表
     * @param <T>       泛型类型
     */
    public <T> void downloadExcel(List<T> dataList, Class<T> clazz, HttpServletResponse response, String fileName) {
        try(Workbook workbook = new SXSSFWorkbook()) {
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()) + "\"");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/vnd.ms-excel");
            setData(workbook, "Sheet1", dataList, clazz, response.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static <T> void setData(Workbook workbook, String sheetName, List<T> dataList, Class<T> clazz, OutputStream os) {
        if (CollectionUtils.isEmpty(dataList)) {
            dataList = Collections.emptyList();
        }
        List<FieldInfo> fieldInfos = getFieldInfos(clazz);
        Sheet sheet = workbook.createSheet(sheetName);
        // 1. 创建表头
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < fieldInfos.size(); i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(fieldInfos.get(i).headerName);
        }
        // 2. 填充数据
        for (int rowIdx = 0; rowIdx < dataList.size(); rowIdx++) {
            T data = dataList.get(rowIdx);
            Row row = sheet.createRow(rowIdx + 1);
            for (int colIdx = 0; colIdx < fieldInfos.size(); colIdx++) {
                FieldInfo fieldInfo = fieldInfos.get(colIdx);
                Object value = fieldInfo.getValue(data); // 通过 MethodHandle 获取值
                Cell cell = row.createCell(colIdx);
                setCellValue(cell, value);
            }
        }
        try {
            workbook.write(os);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取 Class 的字段信息（带缓存）
     */
    private static List<FieldInfo> getFieldInfos(Class<?> clazz) {
        return FIELD_CACHE.computeIfAbsent(clazz, k -> {
            List<FieldInfo> fieldInfos = new ArrayList<>();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(ExcelProperty.class)) {
                    fieldInfos.add(new FieldInfo(field));
                }
            }
            fieldInfos.sort(Comparator.comparingInt(f -> f.order));
            return Collections.unmodifiableList(fieldInfos); // 返回不可变列表
        });
    }

    /**
     * 设置单元格的值（支持 String, Number, Boolean）
     */
    /**
     * 设置单元格值
     */
    private static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            cell.setCellValue("");
        } else if (value instanceof String) {
            cell.setCellValue((String) value);
        } else if (value instanceof Number) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof java.util.Date) {
            cell.setCellValue((java.util.Date) value);
        } else {
            cell.setCellValue(value.toString());
        }
    }

    /**
     * 字段信息（封装反射操作）
     */
    private static class FieldInfo {
        private final String headerName;
        private final int order;
        private final MethodHandle getter; // 使用 MethodHandle 替代反射

        public FieldInfo(Field field) {
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            this.headerName = annotation.value();
            this.order = annotation.order();

            try {
                field.setAccessible(true); // 只需设置一次
                this.getter = MethodHandles.lookup().unreflectGetter(field);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Failed to create MethodHandle for field: " + field.getName(), e);
            }
        }

        public Object getValue(Object obj) {
            try {
                return getter.invoke(obj);
            } catch (Throwable e) {
                throw new RuntimeException("Failed to get field value", e);
            }
        }
    }




    // --------------------------------------- 基于表头导出 -----------------------------------------------
    public static void exportExcel(String filePath, List<String> headers, List<Map<String, Object>> dataList) {
        // 创建工作簿
        File file = new File(filePath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        try(Workbook workbook = new SXSSFWorkbook();
            FileOutputStream fos = new FileOutputStream(file)) {
            setData(workbook, "Sheet1", headers, dataList);
            workbook.write(fos);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 导出Excel到浏览器下载
     *
     * @param response   HttpServletResponse
     * @param fileName   文件名（不带扩展名）
     * @param headers    表头数组，格式为 {"列名1", "列名2", ...}
     * @param dataList   数据列表，每个Map对应一行数据，key与headers中的列名对应
     * @throws IOException
     */
    public static void downloadExcel(HttpServletResponse response, String fileName, List<String> headers, List<Map<String, Object>> dataList) {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

        // 创建工作簿（使用SXSSFWorkbook处理大数据量）
        // 使用SXSSFWorkbook处理大数据量（窗口大小为100行）
        try(Workbook workbook = new SXSSFWorkbook()) {
            setData(workbook, "Sheet1", headers, dataList);
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建Excel工作簿
     *
     * @param headers  表头数组
     * @param dataList 数据列表
     * @return Workbook对象
     */
    public static Workbook setData(Workbook workbook, String sheetName, List<String> headers, List<Map<String, Object>> dataList) {
        Sheet sheet = workbook.getSheet(sheetName);
        // 创建表头样式
        CellStyle headerStyle = createHeaderStyle(workbook);
        // 创建数据样式
        CellStyle dataStyle = createDataStyle(workbook);

        // 创建表头行
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < headers.size(); i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers.get(i));
            cell.setCellStyle(headerStyle);
        }

        // 填充数据
        if (!CollectionUtils.isEmpty(dataList)) {
            int rowNum = 1;
            for (Map<String, Object> data : dataList) {
                Row row = sheet.createRow(rowNum++);
                for (int i = 0; i < headers.size(); i++) {
                    Cell cell = row.createCell(i);
                    Object value = data.get(headers.get(i));
                    setCellValue(cell, value);
                    cell.setCellStyle(dataStyle);
                }
            }
        }

        // 自动调整列宽
        autoSizeColumns(sheet, headers.size());
        return workbook;
    }

    /**
     * 创建表头样式
     */
    public static CellStyle createHeaderStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();

        // 设置背景色
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        // 设置填充模式（新版本）
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);

        // 设置边框（新版本）
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setBorderTop(BorderStyle.THIN);

        // 设置字体
        Font font = workbook.createFont();
        font.setBold(true);  // 新版本设置粗体
        font.setFontHeightInPoints((short) 12);
        style.setFont(font);

        // 居中显示
        // 水平对齐（新版本）
        style.setAlignment(HorizontalAlignment.CENTER);

        // 垂直对齐（新版本）
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }

    /**
     * 创建数据样式
     */
    public static CellStyle createDataStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();

        // 设置边框（新版本使用 BorderStyle 枚举）
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        style.setBorderTop(BorderStyle.THIN);

        // 设置字体（新版本 setBoldweight 已弃用，但普通字体不需要特殊设置）
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 11);
        style.setFont(font);

        // 自动换行（保持不变）
        style.setWrapText(true);

        return style;
    }

    /**
     * 自动调整列宽
     */
    public static void autoSizeColumns(Sheet sheet, int columnCount) {
        // 自动调整列宽（仅对前100行进行计算，避免性能问题）
        for (int i = 0; i < columnCount; i++) {
            sheet.autoSizeColumn(i);

            // 限制最大列宽为50个字符宽度，256是单位换算
            int minColumnWidth = 8 * 256, maxColumnWidth = 50 * 256;
            if (sheet.getColumnWidth(i) > maxColumnWidth) {
                sheet.setColumnWidth(i, maxColumnWidth);
            } else {
                if (sheet.getColumnWidth(i) < minColumnWidth) {
                    sheet.setColumnWidth(i, minColumnWidth);
                } else {
                    // 增加额外宽度，使内容显示更完整
                    sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 512);
                }
            }
        }
    }
}
